ContentAreaManager = new function () {
    /**
     * matches main content areas
     * @type RegExp
     *  0 : matching value
     *  1 : -- misc capture group
     *  2 : additional class definitions
     *  3 : -- misc capture group
     *  4 : id
     *  
     *  originally it was
     *      /<div[^>]*class=(['"])contentarea((?:\s?\w*?)*?)\1[^>]*id=(['"])(\w*)\3[^>]*>((?:.*?\n*?.*?)*?)<\/div><!--\s*?contentarea\s*?-->/mi
     *  but crashes the browser when the closing part is not found
     */
    var areaPattern = /<div[^>]*class=(['"])contentarea((?:\s?\w*?)*?)\1[^>]*id=(['"])(\w*)\3[^>]*>/mi;
    var areaEndPattern = /<\/div><!--\s*?contentarea\s*?-->/i;
    
    /**
     * matches typed contents
     * @type RegExp
     *  0 : matching value
     *  1 : -- misc capture group
     *  2 : type name
     *  3 : type params
     *  4 : additional class definitions
     *  5 : -- misc capture group
     *  6 : id
     *  
     *  originally it was
     *      /<div[^>]*class=(['"])contentarea_(\w*)(\[.*?\]){0,1}((?:\s?\w*?)*?)\1[^>]*id=(['"])(\w*)\5[^>]*>((?:.*?\n*?.*?)*?)<\/div><!-- contentarea_\2 -->/mi
     *  but crashes the browser when the closing part is not found
     */
    var editorPattern = /<div[^>]*class=(['"])contentarea_(\w*)(\[.*?\]){0,1}((?:\s?\w*?)*?)\1[^>]*id=(['"])(\w*)\5[^>]*>/mi;
    var editorEndPattern = ['</div><!--\\s+contentarea_','\\s+-->'];
    
    /**
     * title matcher
     * @type RegExp
     *  1 : id
     *  
     *  originally it was
     *      /<!--\s+@start:title\s+@id=(.*?)\s+-->(.*?)<!--\s+@end:title\s+-->/mi
     *  but crashes the browser when the closing part is not found
     */
    var titlePattern = /<!--\s+@start:title\s+@id=(.*?)\s+-->/i;
    var titleEndPattern = /<!--\s+@end:title\s+-->/i;
    
    /**
     * article matcher
     * @type RegExp
     *  1 : type
     *  
     *  originally it was
     *      /<!-- @start:article_(.*?) -->((?:.*?\n*?.*?)*?)<!-- @end:article_\1 -->/mi
     *  but crashes the browser when the closing part is not found
     */
    var articlePattern = /<!--\s+@start:article_(.*?)\s+-->/i;
    var articleEndPattern = ['<!--\\s+@end:article_','\\s+-->'];

    /**
     * matcher registry
     * @type Array
     */
    var handlers = [];
    
    return {
        register : function(typename, handler) {
            if (typename && handler) handlers[typename] = handler;
        },
        
        match : function(str, index) {
            var s = new String(str.substring(index));
            var area = areaPattern.exec(s);
            var closing = area ? areaEndPattern.exec(s) : null;
            
            return area && closing ? new ContentAreaHandler({
                typename : 'contentarea',
                value : s.substring(area.index, closing.index) + closing[0],
                index : area.index,
                cls : area[2],
                id : area[4],
                content : s.substring(area.index + area[0].length, closing.index)
            }) : null;
        },
        
        matchType : function(str, index) {
            var s = str.substring(index);
            var area = editorPattern.exec(s);
            var closing = area ? new RegExp(editorEndPattern[0] + area[2] + editorEndPattern[1], 'i').exec(s) : null;
    
            var handler = area && closing ? handlers[area[2]] : null;
            
            return handler ? new handler({
                typename : 'contentarea_' + area[2],
                value : s.substring(area.index, closing.index) + closing[0],
                index : area.index,
                params : area[3] || '',
                cls : area[4],
                id : area[6],
                content : s.substring(area.index + area[0].length, closing.index)
            }) : null;
        },
        
        matchTitle : function(str, index) {
            var s = str.substring(index);
            var title = titlePattern.exec(s);
            var closing = title ? titleEndPattern.exec(s) : null;
            
            return title && closing ? new TitleSectionHandler({
                typename : 'title',
                value : s.substring(title.index, closing.index) + closing[0],
                index : title.index,
                id : title[1],
                content : s.substring(title.index + title[0].length, closing.index)
            }) : null;
        },
        
        matchArticle : function(str, index) {
            var s = str.substring(index);
            var area = articlePattern.exec(s);
            var closing = area ? new RegExp(articleEndPattern[0] + area[1] + articleEndPattern[1], 'i').exec(s) : null;
            
            var handler = area && closing ? handlers['article_' + area[1]] : null;
            
            return handler ? new handler({
                typename : 'article_' + area[1],
                value : s.substring(area.index, closing.index) + closing[0],
                index : area.index,
                content : s.substring(area.index + area[0].length, closing.index)
            }) : null;
        },
        
        parse : function(s, matcher) {
            var idx = 0;
            var list = [];
            var h = null;
            while (idx < s.length) {
                h = matcher(s, idx);
                if (!h) break;
                list.push(h);
                idx += h.value.length;
                idx += h.index;
            }
            return list;
        },
        
        buildContent : function(s, list) {
            var idx = 0;
            var h = null;
            var template = '';
            
            for (var i = 0; i < list.length; i++) {
                h = list[i];
                template += s.substr(idx, h.index);
                template += h.getValue();
                idx += h.value.length;
                idx += h.index;
            }
            if (idx < s.length) template += s.substring(idx);
            
            return template;
        },
        
        buildEditorBundle : function(list) {
            var editors = [];
            for (var i = 0; i < list.length; i++) {
                editors = editors.concat(list[i].getEditorBundle());
            }
            
            return editors;
        }
    };
};

AbstractAreaHandler = function(config) {
    Ext.apply(this, config);
    this.init();
};

AbstractAreaHandler.prototype = {
    // instance vars
    value : null,
    index : null,
    params : null,
    cls: null,
    id : null,
    content : null,
    typename : null,
    
    handlers : null,
    editor : null,
    editorBundle : null,
    editorBundleHeader : null,
    editorBundleFooter : null,
    
    init : function() {
    },
    
    getHandlerEditorBundle : function() {
        if (!this.editorBundle) {
            this.editorBundle = [{ html : this.editorBundleHeader, border : false }];
            for (var i = 0; i < this.handlers.length; i++) {
                var editors = this.handlers[i].getEditorBundle();
                this.editorBundle = this.editorBundle.concat(editors);
            }
            this.editorBundle.push({ html : this.editorBundleFooter, border : false });
        }
        
        return this.editorBundle;
    },
    
    getOwnEditorBundle : function() {
        if (!this.editorBundle) {
            this.editorBundle = [
                { html : this.editorBundleHeader, border : false },
                this.getEditor(),
                { html : this.editorBundleFooter, border : false }                
            ];
        }
        
        return this.editorBundle;
    },
    
    getEditorBundle : function() {
        return (this.handlers ? this.getHandlerEditorBundle() : this.getOwnEditorBundle());
    },

    getHandlerEditor : function() {
        if (this.editor == null) {
            var editors = [];
            for (var i = 0; i < this.handlers.length; i++) {
                editors.push(this.handlers[i].getEditor());
            }

            this.editor = new Ext.form.FieldSet({
                items : editors
            });
        }
        
        return this.editor;
    },
    
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextField({
                fieldLabel : this.typename + ' ' + this.id,
                value : this.content
            });
        }
        
        return this.editor;
    },
    
    getEditor : function() {
        return (this.handlers ? this.getHandlerEditor() : this.getOwnEditor());
    },
    
    getHandlerContent : function() {
        return ContentAreaManager.buildContent(this.content, this.handlers);
    },
    
    getOwnContent : function() {
        return (this.editor ? this.editor.getValue() : this.content);
    },
    
    getContent : function() {
        return (this.handlers ? this.getHandlerContent() : this.getOwnContent());
    },
    
    getValue : function() {
        var template = '<div class="' + this.typename;
        if (this.params) template += this.params;
        template += this.cls + '"' + (this.id ? ' id="' + this.id + '">' : '>');
        template += this.getContent();
        template += '</div><!-- ' + this.typename + ' -->';

        return template;
    }
};

ContentAreaHandler = Ext.extend(AbstractAreaHandler, {
    init : function() {
        this.handlers = ContentAreaManager.parse(this.content, ContentAreaManager.matchType);
    }
});

TextAreaHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    }
});

ImageAreaHandler = Ext.extend(AbstractAreaHandler, {
    /**
     * Asset folder name matcher
     * @type RegExp
     *  1 : name of the asset folder
     */
    folderPattern : /\[imageFolder=(.*?)\]/i,

    /**
     * Named binary content matcher
     * @type RegExp
     *  1 : bc param
     *  2 : name of the binary content
     */
    bcNamePattern : /<!-- @emp\.image([^\]*?]){0,1}:begin -->(.*?)<!-- @emp\.image:end -->/i,

    /**
     * Binary content id matcher
     * @type RegExp
     *  1 : bc param
     */
    bcIdPattern : /<img.*?src=".*?\/binarycontent?bcId=(\D*?)".*?>/i,
    
    bcFolderName : null,
    bcName : null,
    bcId : null,

    init : function() {
        var result = this.folderPattern.exec(this.params);
        this.bcFolderName = result ? result[1] : null;
        result = this.bcNamePattern.exec(this.content);
        this.bcName = result ? result[2] : null;
        result = this.bcIdPattern.exec(this.content);
        this.bcId = result ? result[1] : null;
    }
});

RemoteImageAreaHandler = Ext.extend(AbstractAreaHandler, {
    /**
     * Image url matcher
     * @type RegExp
     *  1 : -- misc capture group
     *  2 : image url
     */
    urlPattern : /<img.*?src=(["'])(.*?)\1.*?>/i,
    
    url : null,

    init : function() {
        var result = this.urlPattern.exec(this.content);
        this.url = result ? result[2] : null;
    },

    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextField({
                value : this.url
            });
        }
        
        return this.editor;
    },
    
    getValue : function() {
        return this.value.replace(this.url, this.editor ? this.editor.getValue() : this.url);
    }
});

LinkAreaHandler = Ext.extend(AbstractAreaHandler, {
    /**
     * Link matcher
     * @type RegExp
     *  1 : -- misc capture group
     *  2 : link url
     *  3 : link text
     */
    linkPattern : /<a.*?href=(["'])(.*?)\1.*?>(.*?)<\/a>/i,
    
    url : null,
    text : null,

    init : function() {
        var result = this.linkPattern.exec(this.content);
        this.url = result ? result[2] : null;
        this.text = result ? result[3] : null;
    },

    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextField({
                value : this.url
            });
        }
        
        return this.editor;
    },
    
    getValue : function() {
        return this.value.replace(this.url, this.editor ? this.editor.getValue() : this.url);
    }
});

PortletConfigAreaHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    }
});

SurveyAreaHandler = Ext.extend(AbstractAreaHandler, {
    surveyPattern : /<div.*?(?:readonly=(["'])(true|false)\1)?>/mi,
    readonly : false,

    init : function() {
        var result = this.surveyPattern.exec(this.value);
        this.readonly = result && result[2] && result[2].equals('true');
    },
    
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    }
});

SvgAreaHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    }
});

RemoteSvgAreaHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    }
});

TitleSectionHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextField({
                value : this.content
            });
        }
        
        return this.editor;
    },
    
    getValue : function() {
        return '<!-- @start:title @id=' + this.id + ' -->' +
               this.getContent() +
               '<!-- @end:title -->';
    }
});

TitleAreaHandler = Ext.extend(AbstractAreaHandler, {
    init : function() {
        this.handlers = ContentAreaManager.parse(this.content, ContentAreaManager.matchTitle);
    }
});

ArticleCategorySectionHandler = Ext.extend(AbstractAreaHandler, {
    getValue : function() {
        return '<!-- @start:' + this.typename + ' -->' +
               this.getContent() +
               '<!-- @end:' + this.typename + ' -->';
    }
});

ArticleImageSectionHandler = Ext.extend(AbstractAreaHandler, {
    getValue : function() {
        return '<!-- @start:' + this.typename + ' -->' +
               this.getContent() +
               '<!-- @end:' + this.typename + ' -->';
    }
});

ArticleTitleSectionHandler = Ext.extend(AbstractAreaHandler, {
    getValue : function() {
        return '<!-- @start:' + this.typename + ' -->' +
               this.getContent() +
               '<!-- @end:' + this.typename + ' -->';
    }
});

ArticleLinkSectionHandler = Ext.extend(AbstractAreaHandler, {
    getValue : function() {
        return '<!-- @start:' + this.typename + ' -->' +
               this.getContent() +
               '<!-- @end:' + this.typename + ' -->';
    }
});

ArticleShortcopySectionHandler = Ext.extend(AbstractAreaHandler, {
    getOwnEditor : function() {
        if (this.editor == null) {
            this.editor = new Ext.form.TextArea({
                value : this.content
            });
        }
        
        return this.editor;
    },
    
    getValue : function() {
        return '<!-- @start:' + this.typename + ' -->' +
               this.getContent() +
               '<!-- @end:' + this.typename + ' -->';
    }
});

ArticleAreaHandler = Ext.extend(AbstractAreaHandler, {
    init : function() {
        this.handlers = ContentAreaManager.parse(this.content, ContentAreaManager.matchArticle);
    }
});


ContentAreaManager.register('text', TextAreaHandler);
ContentAreaManager.register('image', ImageAreaHandler);
ContentAreaManager.register('image_R', RemoteImageAreaHandler);
ContentAreaManager.register('link', LinkAreaHandler);
ContentAreaManager.register('portletconfig_attributes', PortletConfigAreaHandler);
ContentAreaManager.register('surveypage', SurveyAreaHandler);
ContentAreaManager.register('image_SVG', SvgAreaHandler);
ContentAreaManager.register('image_SVG_R', RemoteSvgAreaHandler);
ContentAreaManager.register('title', TitleAreaHandler);
ContentAreaManager.register('article', ArticleAreaHandler);
ContentAreaManager.register('article_category', ArticleCategorySectionHandler);
ContentAreaManager.register('article_image', ArticleImageSectionHandler);
ContentAreaManager.register('article_title', ArticleTitleSectionHandler);
ContentAreaManager.register('article_link', ArticleLinkSectionHandler);
ContentAreaManager.register('article_shortcopy', ArticleShortcopySectionHandler);

/*
var handlers = [];
var template = '';

Ext.onReady(function() {
    Ext.Ajax.request({
        url : 'template.html',
        success : function(response, opt) {
            var s = response.responseText;
            var handlers = ContentAreaManager.parse(s, ContentAreaManager.match);
        },
        failure : function(response, opt) {
            alert(response.status);
        }
    });
    var s = document.getElementById("content").innerHTML;

    var handlers = ContentAreaManager.parse(s, ContentAreaManager.match);
    
    var editors = ContentAreaManager.buildEditorBundle(handlers);

    new Ext.Window({
        width : 400,
        height : 500,
        title : 'futty',
        layout : 'fit',
        items : [{
            xtype : 'form',
            items : editors
        }]
    }).show();

    var template = ContentAreaManager.buildContent(s, handlers);
});*/

